﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Configuration;
using System.ComponentModel;

using LibSnesDotNet;

namespace LibSnesDotNet.Input
{
	public class InputSection : ObservableConfigurationSection
	{
		internal static JoypadElement DefaultJoypad1;

		static InputSection()
		{
			DefaultJoypad1 = new JoypadElement();

			DefaultJoypad1.Up = new InputBindingElementCollection();
			DefaultJoypad1.Up.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "Up")});
			DefaultJoypad1.Down = new InputBindingElementCollection();
			DefaultJoypad1.Down.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "Down") });
			DefaultJoypad1.Left = new InputBindingElementCollection();
			DefaultJoypad1.Left.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "Left") });
			DefaultJoypad1.Right = new InputBindingElementCollection();
			DefaultJoypad1.Right.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "Right") });

			DefaultJoypad1.B = new InputBindingElementCollection();
			DefaultJoypad1.B.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "X") });
			DefaultJoypad1.A = new InputBindingElementCollection();
			DefaultJoypad1.A.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "C") });
			DefaultJoypad1.Y = new InputBindingElementCollection();
			DefaultJoypad1.Y.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "Z") });
			DefaultJoypad1.X = new InputBindingElementCollection();
			DefaultJoypad1.X.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "A") });

			DefaultJoypad1.L = new InputBindingElementCollection();
			DefaultJoypad1.L.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "S") });
			DefaultJoypad1.R = new InputBindingElementCollection();
			DefaultJoypad1.R.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "D") });

			DefaultJoypad1.Start = new InputBindingElementCollection();
			DefaultJoypad1.Start.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "V") });
			DefaultJoypad1.Select = new InputBindingElementCollection();
			DefaultJoypad1.Select.Add(new InputBindingElement() { Input = new InputID("RawInput", "Keyboard0", "F") });
		}

		public InputSection()
		{
			SectionInformation.AllowExeDefinition = ConfigurationAllowExeDefinition.MachineToRoamingUser;
			Joypad1 = DefaultJoypad1;
		}

		[ConfigurationProperty("joypad1", IsRequired = true)]
		public JoypadElement Joypad1 { get { return (JoypadElement)this["joypad1"]; } set { this["joypad1"] = value; } }
	}

	public class JoypadElement : ObservableConfigurationElement
	{
		[ConfigurationProperty("up", IsRequired = true)]
		public InputBindingElementCollection Up { get { return (InputBindingElementCollection)this["up"]; } set { this["up"] = value; } }
		[ConfigurationProperty("down", IsRequired = true)]
		public InputBindingElementCollection Down { get { return (InputBindingElementCollection)this["down"]; } set { this["down"] = value; } }
		[ConfigurationProperty("left", IsRequired = true)]
		public InputBindingElementCollection Left { get { return (InputBindingElementCollection)this["left"]; } set { this["left"] = value; } }
		[ConfigurationProperty("right", IsRequired = true)]
		public InputBindingElementCollection Right { get { return (InputBindingElementCollection)this["right"]; } set { this["right"] = value; } }

		[ConfigurationProperty("b", IsRequired = true)]
		public InputBindingElementCollection B { get { return (InputBindingElementCollection)this["b"]; } set { this["b"] = value; } }
		[ConfigurationProperty("a", IsRequired = true)]
		public InputBindingElementCollection A { get { return (InputBindingElementCollection)this["a"]; } set { this["a"] = value; } }
		[ConfigurationProperty("y", IsRequired = true)]
		public InputBindingElementCollection Y { get { return (InputBindingElementCollection)this["y"]; } set { this["y"] = value; } }
		[ConfigurationProperty("x", IsRequired = true)]
		public InputBindingElementCollection X { get { return (InputBindingElementCollection)this["x"]; } set { this["x"] = value; } }

		[ConfigurationProperty("l", IsRequired = true)]
		public InputBindingElementCollection L { get { return (InputBindingElementCollection)this["l"]; } set { this["l"] = value; } }
		[ConfigurationProperty("r", IsRequired = true)]
		public InputBindingElementCollection R { get { return (InputBindingElementCollection)this["r"]; } set { this["r"] = value; } }

		[ConfigurationProperty("start", IsRequired = true)]
		public InputBindingElementCollection Start { get { return (InputBindingElementCollection)this["start"]; } set { this["start"] = value; } }
		[ConfigurationProperty("select", IsRequired = true)]
		public InputBindingElementCollection Select { get { return (InputBindingElementCollection)this["select"]; } set { this["select"] = value; } }
	}

	public class InputBindingElementCollection : ObservableConfigurationElementCollection
	{
		public InputBindingElementCollection()
		{
			AddElementName = "bind";
			RemoveElementName = "unbind";
			ClearElementName = "clearBindings";
		}

		public InputBindingElement this[int index]
		{
			get { return (InputBindingElement)BaseGet(index); }
			set
			{
				BaseRemoveAt(index);
				BaseAdd(index, value);
			}
		}

		protected override ConfigurationElement CreateNewElement()
		{
			return new InputBindingElement();
		}

		protected override object GetElementKey(ConfigurationElement element)
		{
			return ((InputBindingElement)element).Input;
		}

		public bool HasBindingTo(InputID input)
		{
			foreach (InputBindingElement binding in this)
			{
				if (binding.Input == input) { return true; }
			}

			return false;
		}

		public void Add(InputBindingElement binding)
		{
			BaseAdd(binding);
		}

		public void Remove(InputBindingElement binding)
		{
			BaseRemove(binding.Input);
		}

		public void RemoveAt(int index)
		{
			BaseRemoveAt(index);
		}

		public void Clear()
		{
			BaseClear();
		}
	}

	public class InputBindingElement : ObservableConfigurationElement
	{
		[ConfigurationProperty("input", IsRequired = true, IsKey = true)]
		public InputID Input { get { return (InputID)this["input"]; } set { this["input"] = value; } }
	}

}
